//
// Created by baigui on 2021/10/29.
//

#ifndef SIMPLE_PLAYER_TSFILTER_H
#define SIMPLE_PLAYER_TSFILTER_H

#include <cstdint>
#include <libavutil/intreadwrite.h>
#include <FFMPEGDemux.h>
#include "PESContext.h"

enum MpegTSFilterType {
    MPEGTS_PES,
    MPEGTS_SECTION,
    MPEGTS_PCR,
};
//PAT的头， 基本没啥卵用。
typedef struct SectionHeader {
    uint8_t tid;
    uint16_t id;
    uint8_t version;
    uint8_t sec_num;
    uint8_t last_sec_num;
} SectionHeader;

static inline int get8(const uint8_t **pp, const uint8_t *p_end)
{
    const uint8_t *p;
    int c;

    p = *pp;
    if (p >= p_end)
        return -1;
    c   = *p++;
    *pp = p;
    return c;
}

static inline int get16(const uint8_t **pp, const uint8_t *p_end)
{
    const uint8_t *p;
    int c;

    p = *pp;
    if (1 >= p_end - p)
        return -1;
    c   = AV_RB16(p);
    p  += 2;
    *pp = p;
    return c;
}

static int parse_section_header(SectionHeader *h,
                                const uint8_t **pp, const uint8_t *p_end)
{
    int val;

    val = get8(pp, p_end);
    if (val < 0)
        return val;
    h->tid = val;
    *pp += 2;
    val  = get16(pp, p_end);
    if (val < 0)
        return val;
    h->id = val;
    val = get8(pp, p_end);
    if (val < 0)
        return val;
    h->version = (val >> 1) & 0x1f;
    val = get8(pp, p_end);
    if (val < 0)
        return val;
    h->sec_num = val;
    val = get8(pp, p_end);
    if (val < 0)
        return val;
    h->last_sec_num = val;
    return 0;
}

class SectionFilterCallBack{
public:
  virtual void OnPatCallBack(int pmtPid,int id) = 0;
    virtual void OnPmtCallBack(int pmtPid,MediaStream *stream) = 0;

};

class TsBaseFilter {

public:
    virtual int process(const uint8_t *packet ,int len, int is_start) = 0;

public:
    int pid;
    int es_id;
    int last_cc; /* last cc code (-1 if first packet) */
    int64_t last_pcr;
    enum MpegTSFilterType type;
};

typedef  enum SectionFilterType {
    PAT,
    PMT,
    SDT,
} SectionFilterType;

class TsSectionFilter : public TsBaseFilter{
public:
    TsSectionFilter(SectionFilterType type);
    int process(const uint8_t *packet ,int len,int is_start) override;
    int section_index{};
    int section_h_size{};
    int last_ver{};
    unsigned crc{};
    unsigned last_crc{};
    uint8_t *section_buf{};
    unsigned int check_crc : 1;
    unsigned int end_of_section_reached : 1;
    SectionFilterType mSectionType;
    void *opaque{};
    void setSectionFilterCallBack(SectionFilterCallBack *callBack);

private:
    bool tid{};
    SectionFilterCallBack *mSectionFilterCallBack{};
};

enum MpegTSState {
    MPEGTS_HEADER = 0,
    MPEGTS_PESHEADER,
    MPEGTS_PESHEADER_FILL,
    MPEGTS_PAYLOAD,
    MPEGTS_SKIP,
};

#define PES_START_SIZE  6
#define PES_HEADER_SIZE 9
#define MAX_PES_HEADER_SIZE (9 + 255)

#define MAX_PES_PAYLOAD 200 * 1024

class TsPESFilter : public TsBaseFilter{
public:
    PESContext *mPESContext;
    int process(const uint8_t *packet ,int len, int is_start) override;
    MpegTSState state;

    bool pkt;
    int data_index;
    int stop_parse;
    uint8_t header[MAX_PES_HEADER_SIZE];
    uint8_t stream_id;
    int total_size;
    block_t *buffer;
    int pes_header_size;
    int64_t pts;
    int extended_stream_id;
    int stream_type;
    int64_t dts;
};

/**
 * Parse MPEG-PES five-byte timestamp
 */
static inline int64_t ff_parse_pes_pts(const uint8_t *buf) {
    return (int64_t)(*buf & 0x0e) << 29 |
           (AV_RB16(buf+1) >> 1) << 15 |
           AV_RB16(buf+3) >> 1;
}

static void reset_pes_packet_state(TsPESFilter *pes)
{
    pes->pts        = AV_NOPTS_VALUE;
//    pes->dts        = AV_NOPTS_VALUE;
    pes->data_index = 0;
//    pes->flags      = 0;
    delete (&pes->buffer);
}

static int skip_identical(const SectionHeader *h, TsSectionFilter *tssf)
{
    if (h->version == tssf->last_ver && tssf->last_crc == tssf->crc)
        return 0;

    tssf->last_ver = h->version;
    tssf->last_crc = tssf->crc;

    return 0;
}

#endif //SIMPLE_PLAYER_TSFILTER_H
